/****************************************************************************
**
** This file is part of the KD Chart library.
**
** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
**
** SPDX-License-Identifier: MIT
**
****************************************************************************/

#ifndef KDCHARTABSTRACTDIAGRAM_P_H
#define KDCHARTABSTRACTDIAGRAM_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the KD Chart API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include "KDChartAbstractCoordinatePlane.h"
#include "KDChartAbstractDiagram.h"
#include "KDChartBackgroundAttributes.h"
#include "KDChartChart.h"
#include "KDChartDataValueAttributes.h"
#include "KDChartPaintContext.h"
#include "KDChartPosition.h"
#include "KDChartPrintingParameters.h"
#include "KDChartRelativePosition.h"
#include "ReverseMapper.h"
#include <KDChartCartesianDiagramDataCompressor_p.h>

#include <QFont>
#include <QFontMetrics>
#include <QMap>
#include <QModelIndex>
#include <QPaintDevice>
#include <QPainterPath>
#include <QPoint>
#include <QPointer>

namespace KDChart {
class LabelPaintInfo
{
public:
    LabelPaintInfo();
    LabelPaintInfo(const QModelIndex &_index, const DataValueAttributes &_attrs,
                   const QPainterPath &_labelArea, const QPointF &_markerPos,
                   bool _isValuePositive, const QString &_value);
    LabelPaintInfo(const LabelPaintInfo &other);
    QModelIndex index;
    DataValueAttributes attrs;
    QPainterPath labelArea;
    QPointF markerPos;
    bool isValuePositive = false;
    // could (ab)use attrs.dataLabel() instead
    QString value;
};

class LabelPaintCache
{
public:
    LabelPaintCache()
    {
    }
    ~LabelPaintCache()
    {
        clear();
    }

    void clear()
    {
        paintReplay.clear();
    }

    QVector<LabelPaintInfo> paintReplay;

private:
    LabelPaintCache(LabelPaintCache &other); // no copies
};

/**
 * \internal
 */
class AttributesModel;

class KDChart::AbstractDiagram::Private
{
    friend class AbstractDiagram;

public:
    explicit Private();
    virtual ~Private();

    Private(const Private &rhs);

    void setAttributesModel(AttributesModel *);

    bool usesExternalAttributesModel() const;

    // FIXME: Optimize if necessary
    virtual qreal calcPercentValue(const QModelIndex &index) const;

    // this should possibly be virtual so it can be overridden
    void addLabel(LabelPaintCache *cache,
                  const QModelIndex &index,
                  const CartesianDiagramDataCompressor::CachePosition *position,
                  const PositionPoints &points, const Position &autoPositionPositive,
                  const Position &autoPositionNegative, const qreal value,
                  qreal favoriteAngle = 0.0);

    const QFontMetrics *cachedFontMetrics(const QFont &font, const QPaintDevice *paintDevice) const;
    const QFontMetrics cachedFontMetrics() const;

    QString formatNumber(qreal value, int decimalDigits) const;
    QString formatDataValueText(const DataValueAttributes &dva,
                                const QModelIndex &index, qreal value) const;

    void forgetAlreadyPaintedDataValues();

    void paintDataValueTextsAndMarkers(PaintContext *ctx,
                                       const LabelPaintCache &cache,
                                       bool paintMarkers,
                                       bool justCalculateRect = false,
                                       QRectF *cumulatedBoundingRect = nullptr);

    void paintDataValueText(QPainter *painter,
                            const QModelIndex &index,
                            const QPointF &pos,
                            qreal value,
                            bool justCalculateRect = false,
                            QRectF *cumulatedBoundingRect = nullptr);

    void paintDataValueText(QPainter *painter,
                            const DataValueAttributes &attrs,
                            const QPointF &pos,
                            bool valueIsPositive,
                            const QString &text,
                            bool justCalculateRect = false,
                            QRectF *cumulatedBoundingRect = nullptr);

    inline int datasetCount() const
    {
        return attributesModel->columnCount(attributesModelRootIndex) / datasetDimension;
    }

    virtual QModelIndex indexAt(const QPoint &point) const;

    QModelIndexList indexesAt(const QPoint &point) const;

    QModelIndexList indexesIn(const QRect &rect) const;

    virtual CartesianDiagramDataCompressor::AggregatedDataValueAttributes aggregatedAttrs(
        const QModelIndex &index,
        const CartesianDiagramDataCompressor::CachePosition *position) const;

    /**
     * Sets arbitrary attributes of a data set.
     */
    void setDatasetAttrs(int dataset, const QVariant &data, int role);

    /**
     * Retrieves arbitrary attributes of a data set.
     */
    QVariant datasetAttrs(int dataset, int role) const;

    /**
     * Resets an attribute of a dataset back to its default.
     */
    void resetDatasetAttrs(int dataset, int role);

    /**
     * Whether the diagram is transposed (X and Y swapped), which has the same effect as rotating
     * the diagram 90° clockwise and inverting the (then vertical) X coordinate.
     */
    bool isTransposed() const;

    static Private *get(AbstractDiagram *diagram)
    {
        return diagram->_d;
    }

    AbstractDiagram *diagram = nullptr;
    ReverseMapper reverseMapper;
    bool doDumpPaintTime = false; // for use in performance testing code

protected:
    void init();
    void init(AbstractCoordinatePlane *plane);

    QPointer<AbstractCoordinatePlane> plane;
    mutable QModelIndex attributesModelRootIndex;
    QPointer<AttributesModel> attributesModel;
    bool allowOverlappingDataValueTexts = false;
    bool antiAliasing = true;
    bool percent = false;
    int datasetDimension = 1;
    mutable QPair<QPointF, QPointF> databoundaries;
    mutable bool databoundariesDirty = true;

    QMap<Qt::Orientation, QString> unitSuffix;
    QMap<Qt::Orientation, QString> unitPrefix;
    QMap<int, QMap<Qt::Orientation, QString>> unitSuffixMap;
    QMap<int, QMap<Qt::Orientation, QString>> unitPrefixMap;
    QList<QPainterPath> alreadyDrawnDataValueTexts;

private:
    QString prevPaintedDataValueText;
    mutable QFontMetrics mCachedFontMetrics;
    mutable QFont mCachedFont;
    mutable QPaintDevice *mCachedPaintDevice;
};

inline AbstractDiagram::AbstractDiagram(Private *p)
    : _d(p)
{
    init();
}
inline AbstractDiagram::AbstractDiagram(
    Private *p, QWidget *parent, AbstractCoordinatePlane *plane)
    : QAbstractItemView(parent)
    , _d(p)
{
    _d->init(plane);
    init();
}

class LineAttributesInfo
{
public:
    LineAttributesInfo();
    LineAttributesInfo(const QModelIndex &_index, const QPointF &_value, const QPointF &_nextValue);

    QModelIndex index;
    QPointF value;
    QPointF nextValue;
};

typedef QVector<LineAttributesInfo> LineAttributesInfoList;
typedef QVectorIterator<LineAttributesInfo> LineAttributesInfoListIterator;
}
#endif /* KDCHARTDIAGRAM_P_H */
